﻿namespace Json
{
  #region Namespace Imports

  using System;
  using System.Globalization;

  #endregion


  public class Number
  {
    #region Constants and Fields

    private readonly NumberFormatInfo _numberFormat = NumberFormatInfo.InvariantInfo;
    private readonly string _value;
    private Type _nativeType;
    private object _nativeValue;

    #endregion


    #region Constructors and Destructors

    public Number()
    {
    }

    public Number(Number number)
    {
      _numberFormat = number._numberFormat;
      _value = number._value;
      _nativeType = number._nativeType;
      _nativeValue = number._nativeValue;
    }


    public Number(object value)
    {
      _nativeType = value.GetType();
      _nativeValue = value;
      _value = Convert.ToString(value, _numberFormat);
    }

    public Number(string value)
    {
      _value = value;
    }
    
    public Number(string value, NumberFormatInfo numberFormat)
    {
      _value = value;
      _numberFormat = numberFormat;
    }


    public bool IsNative
    {
      get { return NativeType != null; }
    }

    public Type NativeType
    {
      get { return _nativeType; }
    }

    public object NativeValue
    {
      get
      {
        if (!IsNative)
          throw new MemberAccessException("Native Value can be obtained only if Number is native");
        return _nativeValue;
      }
    }

    #endregion


    #region Public Methods

    public Decimal AsDecimal()
    {
      return Decimal.Parse(_value, _numberFormat);
    }


    public Double AsDouble()
    {
      return Double.Parse(_value, _numberFormat);
    }

    public Byte AsByte()
    {
      return Byte.Parse(_value, NumberStyles.Integer, _numberFormat);
    }

    public SByte AsSByte()
    {
      return SByte.Parse(_value, NumberStyles.Integer, _numberFormat);
    }

    public Int16 AsInt16()
    {
      return Int16.Parse(_value, NumberStyles.Integer, _numberFormat);
    }


    public Int32 AsInt32()
    {
      return Int32.Parse(_value, NumberStyles.Integer, _numberFormat);
    }


    public Int64 AsInt64()
    {
      return Int64.Parse(_value, NumberStyles.Integer, _numberFormat);
    }


    public UInt16 AsUInt16()
    {
      return UInt16.Parse(_value, NumberStyles.Integer, _numberFormat);
    }


    public UInt32 AsUInt32()
    {
      return UInt32.Parse(_value, NumberStyles.Integer, _numberFormat);
    }


    public UInt64 AsUInt64()
    {
      return UInt64.Parse(_value, NumberStyles.Integer, _numberFormat);
    }


    public static bool Supports(Type objectType)
    {
      return objectType == typeof(Number)
             || objectType == typeof(Byte) || objectType == typeof(SByte)
             || objectType == typeof(Int16) || objectType == typeof(UInt16) 
             || objectType == typeof(Int32) || objectType == typeof(UInt32) 
             || objectType == typeof(Int64) || objectType == typeof(UInt64)
             || objectType == typeof(Double) || objectType == typeof(Decimal) 
             || objectType == typeof(Byte?) || objectType == typeof(SByte?)
             || objectType == typeof(Int16?) || objectType == typeof(UInt16?) 
             || objectType == typeof(Int32?) || objectType == typeof(UInt32?)
             || objectType == typeof(Int64?) || objectType == typeof(UInt64?) 
             || objectType == typeof(Double?) || objectType == typeof(Decimal?);

    }


    public object ConvertTo(Type objectType)
    {
      if (objectType == typeof(Number))
        return this;

      if (objectType == typeof(Byte))
      {
        return AsByte();
      }
      if (objectType == typeof(SByte))
      {
        return AsSByte();
      }
      if (objectType == typeof(Int16))
      {
        return AsInt16();
      }
      if (objectType == typeof(UInt16))
      {
        return AsUInt16();
      }

      if (objectType == typeof(Int32))
      {
        return AsInt32();
      }

      if (objectType == typeof(UInt32))
      {
        return AsUInt32();
      }
      if (objectType == typeof(Int64))
      {
        return AsInt64();
      }
      if (objectType == typeof(UInt64))
      {
        return AsUInt64();
      }

      if (objectType == typeof(Double))
      {
        return AsDouble();
      }

      if (objectType == typeof(Decimal))
      {
        return AsDecimal();
      }
      if (objectType == typeof(Byte?))
      {
        return AsByte();
      }
      if (objectType == typeof(SByte?))
      {
        return AsSByte();
      }
      if (objectType == typeof(Int16?))
      {
        return AsInt16();
      }
      if (objectType == typeof(UInt16?))
      {
        return AsUInt16();
      }

      if (objectType == typeof(Int32?))
      {
        return AsInt32();
      }

      if (objectType == typeof(UInt32?))
      {
        return AsUInt32();
      }
      if (objectType == typeof(Int64?))
      {
        return AsInt64();
      }
      if (objectType == typeof(UInt64?))
      {
        return AsUInt64();
      }

      if (objectType == typeof(Double?))
      {
        return AsDouble();
      }

      if (objectType == typeof(Decimal?))
      {
        return AsDecimal();
      }

      throw new JsonException("Can not convert Number to the " + objectType.FullName);
    }


    public override string ToString()
    {
      return _value;
    }

    #endregion
  }
}